#include "lcd_init.h"
#include "stdio.h"
#include <string.h>
// #include "app.h"
#define Command 0
#define Parameter 1
#define ID1 0x59
#define ID2 0x45
#define ID3 0x53
typedef void (*FunType)(void);
//**********************************write function begin********************************************
void wr_cmd_4spi_8bcs(uint8_t par)
{
    LCD_CSLow();
    LCD_SCLLow();
    LCD_SDALow();
    LCD_SCLHigh();
    for (uint8_t i = 0; i < 8; i++)
    {
        LCD_SCLLow();
        if (par & 0x80)
        {
            LCD_SDAHigh();
        }
        else
        {
            LCD_SDALow();
        }
        LCD_SCLHigh();
        par <<= 1;
    }
    LCD_CSHigh();
}
void wr_dat_4spi_8bcs(uint8_t par)
{
    LCD_CSLow();
    LCD_SCLLow();
    LCD_SDAHigh();
    LCD_SCLHigh();
    for (uint8_t i = 0; i < 8; i++)
    {
        LCD_SCLLow();
        if (par & 0x80)
        {
            LCD_SDAHigh();
        }
        else
        {
            LCD_SDALow();
        }
        LCD_SCLHigh();
        par <<= 1;
    }
    LCD_CSHigh();
}

void sda_state(uint8_t state)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (state == 0)
    {
        GPIO_InitStruct.Pin = SPI1_MOSI_Pin;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(SPI1_MOSI_GPIO_Port, &GPIO_InitStruct);
    }
    else if (state == 1)
    {
        GPIO_InitStruct.Pin = SPI1_MOSI_Pin;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(SPI1_MOSI_GPIO_Port, &GPIO_InitStruct);
    }
}

uint8_t rd_spi(uint8_t reg)
{
    sda_state(io_writ);
    uint8_t re_val = 0;
    uint8_t addr = reg;
    int i = 0;

    LCD_CSLow();
    LCD_SCLLow();
    LCD_SDALow();
    LCD_SCLHigh();

    for (i = 0; i < 8; i++)
    {
        LCD_SCLLow();
        if (reg & 0x80)
        {
            LCD_SDAHigh();
        }
        else
        {
            LCD_SDALow();
        }
        reg = reg << 1;
        LCD_SCLHigh();
    }
    sda_state(io_read);
    for (i = 0; i < 8; i++)
    {
        LCD_SCLLow();
        re_val = re_val << 1;
        re_val = re_val + HAL_GPIO_ReadPin(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        LCD_SCLHigh();
    }
    LCD_CSHigh();
    printf("reg %02x is %02X\n\r", addr, re_val);

    sda_state(io_writ);

    return re_val;
}

uint8_t rd_spi_04(void)
{
    sda_state(io_writ);
    uint8_t re_val1 = 0;
    uint8_t re_val2 = 0;
    uint8_t re_val3 = 0;
    uint8_t reg = 0x04;
    uint8_t state = 0;
    int i = 0;

    LCD_CSLow();
    LCD_SCLLow();
    LCD_SDALow();
    LCD_SCLHigh();

    for (i = 0; i < 8; i++)
    {
        LCD_SCLLow();
        if (reg & 0x80)
        {
            LCD_SDAHigh();
        }
        else
        {
            LCD_SDALow();
        }
        reg = reg << 1;
        LCD_SCLHigh();
    }
    sda_state(io_read);

    LCD_SCLLow();
    LCD_SCLHigh();
    for (i = 0; i < 8; i++)
    {
        LCD_SCLLow();
        re_val1 = re_val1 << 1;
        re_val1 = re_val1 + HAL_GPIO_ReadPin(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        LCD_SCLHigh();
    }
    for (i = 0; i < 8; i++)
    {
        LCD_SCLLow();
        re_val2 = re_val2 << 1;
        re_val2 = re_val2 + HAL_GPIO_ReadPin(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        LCD_SCLHigh();
    }
    for (i = 0; i < 8; i++)
    {
        LCD_SCLLow();
        re_val3 = re_val3 << 1;
        re_val3 = re_val3 + HAL_GPIO_ReadPin(SPI1_MOSI_GPIO_Port, SPI1_MOSI_Pin);
        LCD_SCLHigh();
    }
    LCD_CSHigh();
    printf("reg id is %02X %02X %02X\n\r", re_val1, re_val2, re_val3);

    if ((re_val1 == ID1) && (re_val2 == ID2) && (re_val3 == ID3))
    {
        state = 1;
    }
    else
    {
        state = 0;
    }

    sda_state(io_writ);

    return state;
}

void wr_cmd_parall1_8(uint16_t par)
{
    LCD_DCXLow();
    PAR_CSLow();
    GPIOB->ODR = par;

    LCD_WRLow();
    LCD_WRHigh();
    PAR_CSHigh();
}
void wr_dat_parall1_8(uint16_t par)
{
    LCD_DCXHigh();
    PAR_CSLow();
    GPIOB->ODR = par;

    LCD_WRLow();
    LCD_WRHigh();
    PAR_CSHigh();
}
void wr_cmd_parall1_16(uint16_t par)
{
    LCD_DCXLow();
    LCD_CSLow();
    LCD_WRLow();
    GPIOB->ODR = par;

    LCD_WRHigh();
}
void wr_dat_parall1_16(uint16_t par)
{
    LCD_DCXHigh();
    LCD_WRLow();
    GPIOB->ODR = par;

    LCD_WRHigh();
}

void wr_cmd_parall2_8(uint16_t par)
{
    LCD_DCXLow();
    LCD_CSLow();
    LCD_WRLow();
    GPIOB->ODR = par << 1;

    LCD_WRHigh();
}
void wr_dat_parall2_8(uint16_t par)
{
    LCD_DCXHigh();
    LCD_WRLow();
    GPIOB->ODR = par << 1;

    LCD_WRHigh();
}
//**********************************read function begin********************************************
void pb_out(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // db 0-7
    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
                          GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void pb_in(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // db 0-7
    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
                          GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
uint8_t i801_read_byte(void)
{
    uint8_t v;

    LCD_DCXHigh();
    LCD_RDLow();
    v = GPIOB->IDR;
    LCD_RDHigh();

    return v;
}
void rd_parall1_8(uint8_t c, uint8_t *d, uint32_t l)
{
    uint8_t buf[128];

    wr_cmd_parall1_8(c);
    pb_in();
    for (int i = 0; i < l; i++)
    {
        buf[i] = i801_read_byte();
    }
    LCD_CSHigh();
    pb_out();
    memcpy(d, buf, l);
}
//**********************************write display begin********************************************
void WR_1963_REG(uint16_t data)
{
    wr_cmd_parall1_8(data);
}

void WR_1963_PAR(uint16_t data)
{
    wr_dat_parall1_8(data);
}

void WR_7789_REG(uint16_t data)
{
    wr_cmd_4spi_8bcs(data);
}

void WR_7789_PAR(uint16_t data)
{
    wr_dat_4spi_8bcs(data);
}

void delay_ms(uint32_t ms)
{
    HAL_Delay(ms);
}

void init_ST7789V2(void)
{
    WR_7789_REG(0x11); // Exit Sleep
    delay_ms(120);

    WR_7789_REG(0xB0); // RAM Control
    WR_7789_PAR(0x11); // RM=1:RGB interface,DM[1:0]=01:RGB interface DOTCLK SYNC Mode
    WR_7789_PAR(0xF4); // EPF[1:0]=11,ENDIAN=0,MDT[1:0]=01:3 x 6 bytes

    WR_7789_REG(0xB1); // RGB  de mode  ?
    WR_7789_PAR(0x60); // 40 WO=0:Memory,RCM[1:0]=11:RGB HV mode,VS=0,HS=0,CLK=positive edge,DE=1
    WR_7789_PAR(0x04); // VS number
    WR_7789_PAR(0x1E); // HS numbe

    WR_7789_REG(0xB2); // Porch Setting
    WR_7789_PAR(0x0C); // BPA[6:0]: Back porch setting
    WR_7789_PAR(0x0C); // FPA[6:0]: Front porch setting
    WR_7789_PAR(0X00); // PSEN
    WR_7789_PAR(0X33); // BPB[3:0]: Back porch setting,FPB[3:0]: Front porch setting
    WR_7789_PAR(0X33); // BPC[3:0]: Back porch setting,FPC[3:0]: Front porch setting

    WR_7789_REG(0xB7); // Gate Control
    WR_7789_PAR(0x65); // VGHS[2:0]: VGH=14.97V.VGLS[2:0]: VGL=-10.43

    WR_7789_REG(0xBB); // VCOMS Setting
    WR_7789_PAR(0x05); // 0X17

    WR_7789_REG(0xC0); // LCM Control
    WR_7789_PAR(0x2C); // 0,xmy,xbgr,xinv,xmx,xmh,xmv,xgs

    WR_7789_REG(0xC2); // VDV and VRH Command Enable
    WR_7789_PAR(0x01); // Enable

    WR_7789_REG(0xC3); // VRH Set
    WR_7789_PAR(0x13); // 4.3+( vcom+vcom offset+vdv),-4.3+( vcom+vcom offset-vdv)

    WR_7789_REG(0xC4); // DV Set
    WR_7789_PAR(0x20); // 0V

    WR_7789_REG(0xC6); // Frame Rate Control in Normal Mode
    WR_7789_PAR(0x0F); // NLA[2 :0]=0:dot inversion.RTNA[4:0]=0X0F: 60 Hz

    WR_7789_REG(0xD0); // Power Control 1
    WR_7789_PAR(0xA4);
    WR_7789_PAR(0xA1); // AVDD[1:0]=02H:6.6V.AVCL[1:0]=02H:-4.8V.VDS[1:0]=01H:2.3V

    WR_7789_REG(0xD6); // Gate Output Selection in Sleep In Mode
    WR_7789_PAR(0xA1); // GATESEL=0:Gate output is GND in sleep in mode

    WR_7789_REG(0xE0); // Set Gamma1
    WR_7789_PAR(0xD0);
    WR_7789_PAR(0x0C);
    WR_7789_PAR(0x14);
    WR_7789_PAR(0x0C);
    WR_7789_PAR(0x09);
    WR_7789_PAR(0x17);
    WR_7789_PAR(0x39);
    WR_7789_PAR(0x54);
    WR_7789_PAR(0x4F);
    WR_7789_PAR(0x19);
    WR_7789_PAR(0x13);
    WR_7789_PAR(0x13);
    WR_7789_PAR(0x2D);
    WR_7789_PAR(0x34);

    WR_7789_REG(0XE1); // Set Gamma2
    WR_7789_PAR(0xD0);
    WR_7789_PAR(0x13);
    WR_7789_PAR(0x17);
    WR_7789_PAR(0x0A);
    WR_7789_PAR(0x0B);
    WR_7789_PAR(0x05);
    WR_7789_PAR(0x39);
    WR_7789_PAR(0x43);
    WR_7789_PAR(0x4D);
    WR_7789_PAR(0x36);
    WR_7789_PAR(0x11);
    WR_7789_PAR(0x12);
    WR_7789_PAR(0x2C);
    WR_7789_PAR(0x32);

    WR_7789_REG(0x35); // TE
    WR_7789_PAR(0x00);

    WR_7789_REG(0x21); // Display Inversion On

    WR_7789_REG(0x29); // display on
}

void init_SSD1963(void)
{
    // INIT SSD1963
    WR_1963_REG(0xE2); // Set PLL with OSC = 10MHz (hardware)
    WR_1963_PAR(0x1D); // M[7:0] : Multiplier (M) of PLL��VCO = 10 MHz x ( 29 + 1 ) =300 MHz
    WR_1963_PAR(0x02); // N[4:0] : Divider (N) of PLL��PLL frequency = 300 MHz / (2 + 1) =100 MHz
    WR_1963_PAR(0x04); // C[  2] : Effectuate MN valueValidate M and N values
    delay_ms(20);

    WR_1963_REG(0xE0); // Start PLL command
    WR_1963_PAR(0x01); // enable PLL
    delay_ms(10);

    WR_1963_REG(0xE0); // Start PLL command again
    WR_1963_PAR(0x03); // now, use PLL output as system clock
    delay_ms(2);

    WR_1963_REG(0x01); // ����λ
    delay_ms(10);

    WR_1963_REG(0xE6); // ��������Ƶ��,20Mhz(320*240*3*60)
    WR_1963_PAR(0x00); // 2F 00  03
    WR_1963_PAR(0xC0); // FF D9  1E  85
    WR_1963_PAR(0xFF); // FF 16  E9  1d

    WR_1963_REG(0xB0); // set_lcd_mode
    WR_1963_PAR(0x20); // A5=1:24 bit mode
    WR_1963_PAR(0x40); // B[6:5]=10:Serial RGB mode,B[6:5]=11:Serial RGB+dummy mode
    WR_1963_PAR(0x00); // ����LCDˮƽ����
    WR_1963_PAR(0xEF);
    WR_1963_PAR(0x01); // ����LCD��ֱ����
    WR_1963_PAR(0x3F);
    //	WR_1963_PAR(0x1B);//RGB����

    WR_1963_REG(0xB4); // Set front porch and back porch
    WR_1963_PAR(0x01); // HT_H
    WR_1963_PAR(0x67); // HT_L    30+30+240+60-1=359(0x0167)
    WR_1963_PAR(0x00); // HPS_H
    WR_1963_PAR(0x5A); // HPS_L   30+60=90(0x5A)
    WR_1963_PAR(0x1D); // HPW      30-1=29(0x1D)
    WR_1963_PAR(0x00); // LPS_H
    WR_1963_PAR(0x3C); // LPS_L   60(0x3C)
    WR_1963_PAR(0x01); // LPSPP[1:0]the horizontal sync pulse subpixel start position for serial TFT interface

    WR_1963_REG(0xB6); // Set vertical period
    WR_1963_PAR(0x01); // VT_H
    WR_1963_PAR(0x4F); // VT_L		4+4+320+8-1=335(0x014F)
    WR_1963_PAR(0x00); // VPS_H
    WR_1963_PAR(0x0E); // VPS_L   4+8=12(0x0C)
    WR_1963_PAR(0x02); // VPW     4-1=3(0x05)
    WR_1963_PAR(0x00); // FPS_H
    WR_1963_PAR(0x0B); // FPS_L   4(0x08)

    WR_1963_REG(0xF0); // set_pixel_data_interface in the parallel host processor
    WR_1963_PAR(0x00); // A[2:0] : 000 8-bit,Pixel Data Interface Format

    WR_1963_REG(0xBE); // ����PWM���set_pwm_conf
    WR_1963_PAR(0x05); // PWM frequency
    WR_1963_PAR(0xfe); // PWM duty cycle
    WR_1963_PAR(0x01); // 3����C 01

    WR_1963_REG(0x29); // ������ʾ

    WR_1963_REG(0x2A); // set_column_address
    WR_1963_PAR(0x00); // Start column number
    WR_1963_PAR(0x00); //
    WR_1963_PAR(0x00); // End column number
    WR_1963_PAR(0XEF); //

    WR_1963_REG(0x2B); // set_page_address
    WR_1963_PAR(0x00); // Start page (row) number
    WR_1963_PAR(0x00); //
    WR_1963_PAR(0x01); // End page (row) number
    WR_1963_PAR(0X3F); //

    WR_1963_REG(0xB8); // set_gpio_conf
    WR_1963_PAR(0x00); // GPIO3~0 is controlled by host,GPIO3~0 is input
    WR_1963_PAR(0x00); // GPIO0 is used to control the panel power with enter_sleep_mode (0x10) or exit_sleep_mode (0x11)

    WR_1963_REG(0xba); // set_gpio_value
    WR_1963_PAR(0X01); // GPIO0 outputs 1

    WR_1963_REG(0x36); // set_address_mode
    WR_1963_PAR(0x00); // Y,X,XY,

    WR_1963_REG(0x11); // Exit Sleep
    delay_ms(120);
}

void LCD_RESET(void)
{
    LCD_RSTHigh();
    HAL_Delay(10); // ms
    LCD_RSTLow();
    HAL_Delay(10); // ms
    LCD_RSTHigh();
    HAL_Delay(10); // ms
}

void write_cmd(uint16_t par)
{
    wr_cmd_4spi_8bcs(par);
}

void write_data(uint16_t par)
{
    wr_dat_4spi_8bcs(par);
}

void delay(uint32_t ms)
{
    HAL_Delay(ms);
}

void fillcolor(uint8_t dat_r, uint8_t dat_g, uint8_t dat_b)
{
    uint32_t i, j;

    WR_1963_REG(0x2C); // Memory write

    for (i = 0; i < 240; i++)
        for (j = 0; j < 320; j++)
        {
            WR_1963_PAR(dat_r);
            WR_1963_PAR(dat_g);
            WR_1963_PAR(dat_b);
        }
}

void NVM(void)
{
    LCD_RESET(); // H/W Reset
    // write_cmd(0x01);      //ST7789V Software reset
    // delay(120);

    write_cmd(0x10); // write_cmd(0x10);   //ST7789V Sleep In
    delay(10);

    // Waitkey();
    // delay(200);

    HAL_GPIO_WritePin(Ctrl_VPP_GPIO_Port, Ctrl_VPP_Pin, GPIO_PIN_SET); // Supply 7.5V to VPP Pin
    delay(200);                                                        // 10

    write_cmd(0xDF); // Command 2 Enable
    write_data(0x5A);
    write_data(0x69);
    write_data(0x02);
    write_data(0x01); // 0001-->Commands in command table 2 can be executed when EXTC level is “Low”.

    write_cmd(0xFA); // Program Mode Enable
    write_data(0x5A);
    write_data(0x69);
    write_data(0xEE);
    write_data(0x04); // 0100-->Program mode enable

    write_cmd(0xEC);  // Program Mode Control
    write_data(0x01); // When program mode enable, this command need be set.

    write_cmd(0xFC);  // NVM Setting
    write_data(0x0C); // NVM address setting
    write_data(0x2C); // Data setting of NVM address

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    //*****************设定烧录的ID1值*******************//

    write_cmd(0xFC);  // NVM Setting
    write_data(0x0D); // NVM address setting
    write_data(ID1);  // Program ID1 setting

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    //*****************设定烧录的ID2值*******************//

    write_cmd(0xFC);  // NVM Setting
    write_data(0x0E); // NVM address setting
    write_data(ID2);  // Program ID2 setting

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    //*****************设定烧录的ID3值*******************//

    write_cmd(0xFC);  // NVM Setting
    write_data(0x0F); // NVM address setting
    write_data(ID3);  // Program ID3 setting

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    //*****************结束设定烧录的ID值*******************//

    write_cmd(0xFC);  // NVM Setting
    write_data(0x10); //
    write_data(0x01); //

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    write_cmd(0xFC);  // NVM Setting
    write_data(0x11); //
    write_data(0xFF); //

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    write_cmd(0xFC);  // NVM Setting
    write_data(0x12); //
    write_data(0x0B); //

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    write_cmd(0xFC);  // NVM Setting
    write_data(0x13); //
    write_data(0x20); //

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    //*****************设定烧录的VCOM offset voltage*******************//

    write_cmd(0xFC);  // NVM Setting
    write_data(0x14); //
    write_data(0x20); // Program command C5h setting----VCOM offset Voltage//如不需要烧录VCOMOFFSET则填写默认值0x20

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    //*****************结束设定烧录的VCOM offset voltage*******************//

    write_cmd(0xFC);  // NVM Setting
    write_data(0x15); //
    write_data(0x0F); //

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    write_cmd(0xFC);  // NVM Setting
    write_data(0x16); //
    write_data(0x00); //

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    write_cmd(0xFC);  // NVM Setting
    write_data(0x17); //
    write_data(0x08); //

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    write_cmd(0xEC);  // Program Mode Control
    write_data(0x00); // program mode disable

    write_cmd(0xFC);  // NVM Setting
    write_data(0x01); //
    write_data(0xFD); //

    write_cmd(0xFE); // Program action
    write_data(0x29);
    write_data(0xA5);
    delay(5);

    write_cmd(0xFA); // Program Mode Enable
    write_data(0x5A);
    write_data(0x69);
    write_data(0xEE);
    write_data(0x00); // 0100-->Program mode disable

    write_cmd(0xDF); // Command 2 Enable
    write_data(0x5A);
    write_data(0x69);
    write_data(0x02);
    write_data(0x00); // 0001-->Commands in command table 2 can be executed when EXTC level is “Low”.

    // Waitkey();
    // delay(200);

    HAL_GPIO_WritePin(Ctrl_VPP_GPIO_Port, Ctrl_VPP_Pin, GPIO_PIN_RESET); // Remove 7.5v from VPP Pin
    delay(200);                                                          // 100
}

void check_ID(void)
{
    if (rd_spi_04() == 0)
    {
        printf("no OTP\n\r");
        NVM();
        LCD_RESET();
        if (rd_spi_04() == 0)
        {
            init_ST7789V2();
            init_SSD1963();
            fillcolor(0xFF, 0x00, 0x00);
            printf("OTP FAIL\n\r");
            while (1)
            {
                HAL_Delay(100);
            }
        }
        else
        {
            printf("OTP OK\n\r");
        }
    }
    else
    {
        printf("has been OTP\n\r");
    }
}

void lcd_main(void)
{
    LCD_RESET();
    check_ID();
    init_ST7789V2();
    init_SSD1963();
    fillcolor(0x00, 0xFF, 0x00);
    HAL_Delay(1000);
}